import * as THREE from 'three';
import Debug from '../tools/Debug';
import BaseModel from '../BaseModel';
import { loadByUrl } from '../tools/loader';
import ResourceTracker from '../tools/ResourceTracker';

/**
 * @description 获取作物模型的二维插入点坐标
 */
function getCropCoordinate(params: {
  /** 中心xz平面坐标 */
  center: [number, number];
  /** x方向mesh长度 */
  xLength: number;
  /** z方向msh长度 */
  zLength: number;
  /** x方向间距 */
  xSpacing?: number;
  /** z方向间距 */
  zSpacing?: number;
}): Array<{ x: number; y: number }> {
  let { center, xLength, zLength } = params;
  let { xSpacing, zSpacing } = params;
  let _xSpacing = 0;
  let _zSpacing = 0;
  // 基于长边修正默认间距
  if (xLength > zLength) {
    (_xSpacing = Math.floor(xLength / 10)), (_zSpacing = Math.floor(zLength / 2));
  } else {
    (_xSpacing = Math.floor(xLength / 2)), (_zSpacing = Math.floor(zLength / 10));
  }

  xSpacing = xSpacing || _xSpacing;
  zSpacing = zSpacing || _zSpacing;

  const getOffset = (leftBound: number, rightBound: number, spacing: number) => {
    let end = leftBound - spacing / 2;

    for (; end + spacing <= rightBound; end += spacing) {}

    const margin = (spacing / 2 + rightBound - end) / 2;
    return (margin < spacing / 2 ? -1 : 1) * Math.abs(margin - spacing / 2);
  };

  const _getCropCoordinate = (firstPoint: [number, number], thirdPoint: [number, number], xSpacing: number, zSpacing: number) => {
    const [x1, y1] = firstPoint;
    const [x2, y2] = thirdPoint;

    const xMargin = getOffset(x1, x2, xSpacing);
    const yMargin = getOffset(y1, y2, zSpacing);

    const startX = x1 - xSpacing / 2;
    const startY = y1 - zSpacing / 2;

    const cropCoordinate = [];
    for (let x = startX + xSpacing; x <= x2; x += xSpacing) {
      for (let y = startY + zSpacing; y <= y2; y += zSpacing) {
        cropCoordinate.push({
          x: x + xMargin,
          y: y + yMargin,
        });
      }
    }

    if (cropCoordinate.length === 0) {
      return [[(x1 + x2) / 2, (y1 + y2) / 2]];
    }

    return cropCoordinate;
  };

  const [x, z] = center;
  // 矩形的左上角
  const firstPoint = [x - xLength / 2, z - zLength / 2] as [number, number];
  // 矩形的右下角
  const thirdPoint = [x + xLength / 2, z + zLength / 2] as [number, number];
  return _getCropCoordinate(firstPoint, thirdPoint, xSpacing, zSpacing);
}

interface Pos {
  /** 基准点, 缺省 默认(0,0,0) */
  position?: THREE.Vector3Like;
  /** 基准旋转，默认不旋转 */
  quaternion?: THREE.QuaternionLike;
}

interface CropModelInfo {
  url: string;
  /** 默认为1 不缩放 */
  scale?: number;
  /** TODO: xspaing x方向种植间距*/
  // xSpacing?: number;
  /** TODO: zspaing z方向种植间距*/
  // zSpacing?: number;
}

export interface CropRackOption extends Editor.BaseModelOption {
  /** 新增时不传，否则都需要显式传递 */
  id?: number | string;
  /** 父级group */
  cropStaffGroup: Editor.PlantAreaManager['cropStaffGroup'];
  /** 位姿数据 */
  pos?: Pos;
  /** 作物模型信息 */
  cropModelInfo?: CropRackSaveData['cropModelInfo'];
}

export interface CropRackSaveData {
  id: number | string;
  /** 作物架url */
  url: string;
  /** 应该指的是rack的model的scale，而不是group的scale */
  scale: Editor.BaseModelScale;
  /** 位姿数据 */
  pos: Pos;
  /** 作物模型信息 */
  cropModelInfo: CropModelInfo;
}
export default class CropRack extends BaseModel {
  type = 'cropRack';
  /** name为 tu001 tu002 tu003 的mesh可挂载crop模型 */
  static cropMeshNameStartWith = 'tu';

  private readonly namespace = 'cropRack';

  /** 作物模型数组 */
  cropObjects: Array<THREE.Object3D> = [];
  /** 作物模型url */
  cropModelInfo: CropModelInfo = {
    url: null,
    scale: 1,
  };

  group = new THREE.Group();

  /** FIXME: 这里先用模拟写法 */
  id: number | string = Date.now();

  controls: Editor.Controls;

  /** 父级group */
  cropStaffGroup: Editor.PlantAreaManager['cropStaffGroup'];

  cropResourceTracker = new ResourceTracker();

  override init(option: CropRackOption): Promise<THREE.Object3D | null> {
    return new Promise((resolve, reject) => {
      const { url, pos = {}, cropModelInfo, scale = {}, cropStaffGroup, id } = option;
      // 合并必要的baseModel参数
      this.scale = Object.assign(this.scale, scale);
      this.id = id || this.id;
      loadByUrl(url)
        .then((model) => {
          this.resourceTracker = new ResourceTracker();
          this.resourceTracker.track(model);
          this.model = model;

          // 位置预渲染
          pos.position && this.group.position.copy(pos.position);
          pos.quaternion && this.group.quaternion.copy(pos.quaternion);

          // 设置缩放
          this.changeScale(scale);

          // 挂载rack模型
          this.group.name = `cropRack-${this.getId()}`;
          model.name = 'cropRack-model';
          this.group.add(model);

          // 将cropRack的group挂到PlantArea的Group下
          cropStaffGroup.add(this.group);

          // 作物预渲染(当url为null时，则应该不会触发渲染)
          cropModelInfo?.url && this.changeCrop(cropModelInfo);

          Debug.log(this.namespace, '-init-模型加载成功', this.getInfo());
          this._emitEvent(BaseModel.EVENT_BASEMODEL_LOADED, model);
          resolve(model);
        })
        .catch((e) => {
          Debug.error(this.namespace, '-init-模型加载失败', e);
          this._emitEvent(BaseModel.EVENT_BASEMODEL_LOADED, null);
          reject(null);
        });
    });
  }

  constructor(option: CropRackOption) {
    const { cropStaffGroup } = option;

    super(option);
    this.cropStaffGroup = cropStaffGroup;
  }

  controlsChangeHandler = (controls: Editor.Controls) => {
    const { transformControls, orbitControls } = controls;
    // 计算texture
    this._computeTexture(transformControls);
    // 更新scale的值
    if (transformControls.mode === 'scale' && transformControls.object) {
      const currentScale = transformControls.object.scale;
      this.scale = Object.assign(this.scale, {
        x: currentScale.x,
        y: currentScale.y,
        z: currentScale.z,
      });
    }
  };

  private _computeTexture(transformControls: Editor.Controls['transformControls']) {
    if (transformControls.mode !== 'scale') return;
    // TODO:贴图repeat计算
    console.log('执行贴图计算');
  }

  private _renderCrops(cropModel: THREE.Object3D) {
    let counter = 0;
    this.model.traverse((mesh) => {
      if (mesh.name && mesh.name.startsWith(CropRack.cropMeshNameStartWith)) {
        let newModel = null;
        // 第一个模型复用，其余模型克隆
        counter === 0 ? (newModel = cropModel) : (newModel = cropModel.clone());
        const box = new THREE.Box3().setFromObject(mesh);
        //
        const meshWorldCenter = box.getCenter(new THREE.Vector3());
        const meshSize = box.getSize(new THREE.Vector3());

        const cropVec2points = getCropCoordinate({
          center: [meshWorldCenter.x, meshWorldCenter.z],
          xLength: meshSize.x,
          zLength: meshSize.z,
        });
        cropVec2points.forEach((vec2Point) => {
          const { x: xPos, y: zPos } = vec2Point;
          // 克隆模型
          const _newModel = newModel.clone() as THREE.Group;

          // MARK 转换为相对group的local坐标用以抵消旋转副作用
          const worldPos = new THREE.Vector3(xPos, meshWorldCenter.y, zPos);
          const localPos = this.group.worldToLocal(worldPos.clone());

          _newModel.position.copy(localPos);
          _newModel.name = `cropRack-crop-${counter}`;

          // 插入模型
          this.group.add(_newModel);
          this.cropResourceTracker.track(newModel);
          this.cropObjects.push(_newModel);
        });
        counter++;
      }
    });
  }

  /**
   * 更改作物
   * TODO: 功能增强
   *  - 修改作物类型 - 通过url映射
   *  - 修改作物的种植密度 - 通过spacing
   * @param option 作物选项
   */
  changeCrop(option: { url: string; scale?: number }) {
    const { url, scale = 1 } = option;
    if (typeof url !== 'string') return;
    // url相同时不执行
    if (url === this.cropModelInfo?.url) return;
    loadByUrl(url, scale).then((model) => {
      this._renderCrops(model);
      this.cropModelInfo = Object.assign(this.cropModelInfo, option);
    });
  }

  /** @description 移除所有作物模型 */
  removeAllCrops() {
    this.cropObjects.forEach((crop) => {
      this.group.remove(crop);
    });
    this.cropObjects = [];
    this.cropResourceTracker.dispose();
    // 重置cropModelInfo
    this.cropModelInfo = {
      url: null,
      scale: 1,
    };
  }

  /** @description 获取cropPlot的唯一id */
  getId(): string | number {
    return this.id;
  }

  getSaveData(): CropRackSaveData {
    return {
      id: this.getId(),
      scale: this.scale,
      url: this.baseModelOption.url,
      cropModelInfo: this.cropModelInfo,
      pos: {
        position: {
          x: this.group.position.x,
          y: this.group.position.y,
          z: this.group.position.z,
        },
        quaternion: {
          x: this.group.quaternion.x,
          y: this.group.quaternion.y,
          z: this.group.quaternion.z,
          w: this.group.quaternion.w,
        },
      },
    };
  }

  destroy() {
    this.removeAllCrops();
    this.resourceTracker.dispose();
    this.cropStaffGroup.remove(this.group);
    this.resourceTracker = null;
    this.cropResourceTracker = null;
  }
}
